Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

n-api: handle weak no-finalizer refs correctly #34839

Conversation

gabrielschulhof
Copy link
Contributor

When deleting a weak reference that has no finalizer we must not defer
deletion until the non-existent finalizer gets called.

Fixes: #34731

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/n-api

@nodejs-github-bot nodejs-github-bot added the c++ Issues and PRs that require attention from people who are familiar with C++. label Aug 19, 2020
@gabrielschulhof gabrielschulhof added the node-api Issues and PRs related to the Node-API. label Aug 19, 2020
@@ -242,6 +242,8 @@ class RefBase : protected Finalizer, RefTracker {
static inline void Delete(RefBase* reference) {
reference->Unlink();
if ((reference->RefCount() != 0) ||
(reference->RefCount() == 0 &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RefCount() is unsigned, so reference->RefCount() == 0 is always true here, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true. This can be simplified to the second half of the &&.

@gabrielschulhof
Copy link
Contributor Author

gabrielschulhof commented Aug 19, 2020

@addaleax I removed the always-true condition and I also updated the comment above the function to include an explanation of the new condition (i.e. that we want to delete when the ref is weak but has no finalizer).

When deleting a weak reference that has no finalizer we must not defer
deletion until the non-existent finalizer gets called.

Fixes: nodejs#34731
Signed-off-by: Gabriel Schulhof <[email protected]>
@gabrielschulhof
Copy link
Contributor Author

Added Signed-off-by header.

@gabrielschulhof
Copy link
Contributor Author

@gabrielschulhof
Copy link
Contributor Author

Hmmm ... CI is failing, but the logs are truncated, so I can't see what the failure was.

@gabrielschulhof
Copy link
Contributor Author

gabrielschulhof commented Aug 20, 2020

Trott pushed a commit that referenced this pull request Aug 21, 2020
When deleting a weak reference that has no finalizer we must not defer
deletion until the non-existent finalizer gets called.

Fixes: #34731
Signed-off-by: Gabriel Schulhof <[email protected]>

PR-URL: #34839
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
@Trott
Copy link
Member

Trott commented Aug 21, 2020

Landed in acd423b

@Trott Trott closed this Aug 21, 2020
Copy link
Member

@mhdawson mhdawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Belated LGTM

BethGriggs pushed a commit that referenced this pull request Aug 24, 2020
When deleting a weak reference that has no finalizer we must not defer
deletion until the non-existent finalizer gets called.

Fixes: #34731
Signed-off-by: Gabriel Schulhof <[email protected]>

PR-URL: #34839
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
@danielleadams danielleadams mentioned this pull request Aug 25, 2020
danielleadams pushed a commit that referenced this pull request Aug 25, 2020
Notable changes:

- build: set --v8-enable-object-print by default (Mary Marchini)
  [#34705](#34705)
- deps:
  - upgrade to libuv 1.39.0 (cjihrig)
    [#34915](#34915)
  - upgrade npm to 6.14.8 (Ruy Adorno)
    [#34834](#34834)
  - V8: cherry-pick e06ace6b5cdb (Anna Henningsen)
    [#34673](#34673)
- n-api: handle weak no-finalizer refs correctly (Gabriel Schulhof)
  [#34839](#34839)
- tools: add debug entitlements for macOS 10.15+ (Gabriele Greco)
  [#34378](#34378)

PR-URL: #34852
danielleadams pushed a commit that referenced this pull request Aug 26, 2020
Notable changes:

- build: set --v8-enable-object-print by default (Mary Marchini)
  [#34705](#34705)
- deps:
  - upgrade to libuv 1.39.0 (cjihrig)
    [#34915](#34915)
  - upgrade npm to 6.14.8 (Ruy Adorno)
    [#34834](#34834)
  - V8: cherry-pick e06ace6b5cdb (Anna Henningsen)
    [#34673](#34673)
- n-api: handle weak no-finalizer refs correctly (Gabriel Schulhof)
  [#34839](#34839)
- tools: add debug entitlements for macOS 10.15+ (Gabriele Greco)
  [#34378](#34378)

PR-URL: #34852
danielleadams pushed a commit that referenced this pull request Aug 26, 2020
Notable changes:

- build: set --v8-enable-object-print by default (Mary Marchini)
  [#34705](#34705)
- deps:
  - upgrade to libuv 1.39.0 (cjihrig)
    [#34915](#34915)
  - upgrade npm to 6.14.8 (Ruy Adorno)
    [#34834](#34834)
  - V8: cherry-pick e06ace6b5cdb (Anna Henningsen)
    [#34673](#34673)
- n-api: handle weak no-finalizer refs correctly (Gabriel Schulhof)
  [#34839](#34839)
- tools: add debug entitlements for macOS 10.15+ (Gabriele Greco)
  [#34378](#34378)

PR-URL: #34852
danielleadams pushed a commit that referenced this pull request Aug 26, 2020
Notable changes:

- build: set --v8-enable-object-print by default (Mary Marchini)
  [#34705](#34705)
- deps:
  - upgrade to libuv 1.39.0 (cjihrig)
    [#34915](#34915)
  - upgrade npm to 6.14.8 (Ruy Adorno)
    [#34834](#34834)
  - V8: cherry-pick e06ace6b5cdb (Anna Henningsen)
    [#34673](#34673)
- n-api: handle weak no-finalizer refs correctly (Gabriel Schulhof)
  [#34839](#34839)
- tools: add debug entitlements for macOS 10.15+ (Gabriele Greco)
  [#34378](#34378)

PR-URL: #34852
danielleadams pushed a commit that referenced this pull request Aug 27, 2020
Notable changes:

- build: set --v8-enable-object-print by default (Mary Marchini)
  [#34705](#34705)
- deps:
  - upgrade to libuv 1.39.0 (cjihrig)
    [#34915](#34915)
  - upgrade npm to 6.14.8 (Ruy Adorno)
    [#34834](#34834)
  - V8: cherry-pick e06ace6b5cdb (Anna Henningsen)
    [#34673](#34673)
- n-api: handle weak no-finalizer refs correctly (Gabriel Schulhof)
  [#34839](#34839)
- tools: add debug entitlements for macOS 10.15+ (Gabriele Greco)
  [#34378](#34378)

PR-URL: #34852
BethGriggs pushed a commit that referenced this pull request Aug 27, 2020
Notable changes:

- build: set --v8-enable-object-print by default (Mary Marchini)
  [#34705](#34705)
- deps:
  - upgrade to libuv 1.39.0 (cjihrig)
    [#34915](#34915)
  - upgrade npm to 6.14.8 (Ruy Adorno)
    [#34834](#34834)
  - V8: cherry-pick e06ace6b5cdb (Anna Henningsen)
    [#34673](#34673)
- n-api: handle weak no-finalizer refs correctly (Gabriel Schulhof)
  [#34839](#34839)
- tools: add debug entitlements for macOS 10.15+ (Gabriele Greco)
  [#34378](#34378)

PR-URL: #34852
addaleax pushed a commit that referenced this pull request Sep 22, 2020
When deleting a weak reference that has no finalizer we must not defer
deletion until the non-existent finalizer gets called.

Fixes: #34731
Signed-off-by: Gabriel Schulhof <[email protected]>

PR-URL: #34839
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
addaleax pushed a commit that referenced this pull request Sep 22, 2020
When deleting a weak reference that has no finalizer we must not defer
deletion until the non-existent finalizer gets called.

Fixes: #34731
Signed-off-by: Gabriel Schulhof <[email protected]>

PR-URL: #34839
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
@codebytere codebytere mentioned this pull request Sep 28, 2020
gabrielschulhof pushed a commit to gabrielschulhof/node that referenced this pull request Nov 2, 2020
Currently, a reference is being unlinked from the list of references
tracked by the environment when `v8impl::Reference::Delete` is called.
This causes a leak when deletion must be deferred because the finalizer
hasn't yet run, but the finalizer does not run because environment
teardown is in progress, and so no more gc runs will happen, and the
`FinalizeAll` run that happens during environment teardown does not
catch the reference because it's no longer in the list. The test below
will fail when running with ASAN:
```
./node ./test/node-api/test_worker_terminate_finalization/test.js
```

OTOH if, to address the above leak, we make a special case to not
unlink a reference during environment teardown, we run into a
situation where the reference gets deleted by
`v8impl::Reference::Delete` but does not get unlinked because it's
environment teardown time. This leaves a stale pointer in the linked
list which will result in a use-after-free in `FinalizeAll` during
environment teardown. The test below will fail if we make the above
change:
```
./node -e "require('./test/node-api/test_instance_data/build/Release/test_ref_then_set.node');"
```

Thus, we unlink a reference precisely when we destroy it – in its
destructor.

Refs: nodejs#34731
Refs: nodejs#34839
Refs: nodejs#35620
Refs: nodejs#35777
Fixes: nodejs#35778
Signed-off-by: Gabriel Schulhof <[email protected]>
gabrielschulhof pushed a commit that referenced this pull request Nov 5, 2020
Currently, a reference is being unlinked from the list of references
tracked by the environment when `v8impl::Reference::Delete` is called.
This causes a leak when deletion must be deferred because the finalizer
hasn't yet run, but the finalizer does not run because environment
teardown is in progress, and so no more gc runs will happen, and the
`FinalizeAll` run that happens during environment teardown does not
catch the reference because it's no longer in the list. The test below
will fail when running with ASAN:

```
./node ./test/node-api/test_worker_terminate_finalization/test.js
```

OTOH if, to address the above leak, we make a special case to not
unlink a reference during environment teardown, we run into a
situation where the reference gets deleted by
`v8impl::Reference::Delete` but does not get unlinked because it's
environment teardown time. This leaves a stale pointer in the linked
list which will result in a use-after-free in `FinalizeAll` during
environment teardown. The test below will fail if we make the above
change:

```
./node -e "require('./test/node-api/test_instance_data/build/Release/test_ref_then_set.node');"
```

Thus, we unlink a reference precisely when we destroy it – in its
destructor.

Refs: #34731
Refs: #34839
Refs: #35620
Refs: #35777
Fixes: #35778
Signed-off-by: Gabriel Schulhof <[email protected]>
PR-URL: #35933
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Zeyu Yang <[email protected]>
danielleadams pushed a commit that referenced this pull request Nov 9, 2020
Currently, a reference is being unlinked from the list of references
tracked by the environment when `v8impl::Reference::Delete` is called.
This causes a leak when deletion must be deferred because the finalizer
hasn't yet run, but the finalizer does not run because environment
teardown is in progress, and so no more gc runs will happen, and the
`FinalizeAll` run that happens during environment teardown does not
catch the reference because it's no longer in the list. The test below
will fail when running with ASAN:

```
./node ./test/node-api/test_worker_terminate_finalization/test.js
```

OTOH if, to address the above leak, we make a special case to not
unlink a reference during environment teardown, we run into a
situation where the reference gets deleted by
`v8impl::Reference::Delete` but does not get unlinked because it's
environment teardown time. This leaves a stale pointer in the linked
list which will result in a use-after-free in `FinalizeAll` during
environment teardown. The test below will fail if we make the above
change:

```
./node -e "require('./test/node-api/test_instance_data/build/Release/test_ref_then_set.node');"
```

Thus, we unlink a reference precisely when we destroy it – in its
destructor.

Refs: #34731
Refs: #34839
Refs: #35620
Refs: #35777
Fixes: #35778
Signed-off-by: Gabriel Schulhof <[email protected]>
PR-URL: #35933
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Zeyu Yang <[email protected]>
BethGriggs pushed a commit that referenced this pull request Dec 9, 2020
Currently, a reference is being unlinked from the list of references
tracked by the environment when `v8impl::Reference::Delete` is called.
This causes a leak when deletion must be deferred because the finalizer
hasn't yet run, but the finalizer does not run because environment
teardown is in progress, and so no more gc runs will happen, and the
`FinalizeAll` run that happens during environment teardown does not
catch the reference because it's no longer in the list. The test below
will fail when running with ASAN:

```
./node ./test/node-api/test_worker_terminate_finalization/test.js
```

OTOH if, to address the above leak, we make a special case to not
unlink a reference during environment teardown, we run into a
situation where the reference gets deleted by
`v8impl::Reference::Delete` but does not get unlinked because it's
environment teardown time. This leaves a stale pointer in the linked
list which will result in a use-after-free in `FinalizeAll` during
environment teardown. The test below will fail if we make the above
change:

```
./node -e "require('./test/node-api/test_instance_data/build/Release/test_ref_then_set.node');"
```

Thus, we unlink a reference precisely when we destroy it – in its
destructor.

Refs: #34731
Refs: #34839
Refs: #35620
Refs: #35777
Fixes: #35778
Signed-off-by: Gabriel Schulhof <[email protected]>
PR-URL: #35933
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Zeyu Yang <[email protected]>
BethGriggs pushed a commit that referenced this pull request Dec 10, 2020
Currently, a reference is being unlinked from the list of references
tracked by the environment when `v8impl::Reference::Delete` is called.
This causes a leak when deletion must be deferred because the finalizer
hasn't yet run, but the finalizer does not run because environment
teardown is in progress, and so no more gc runs will happen, and the
`FinalizeAll` run that happens during environment teardown does not
catch the reference because it's no longer in the list. The test below
will fail when running with ASAN:

```
./node ./test/node-api/test_worker_terminate_finalization/test.js
```

OTOH if, to address the above leak, we make a special case to not
unlink a reference during environment teardown, we run into a
situation where the reference gets deleted by
`v8impl::Reference::Delete` but does not get unlinked because it's
environment teardown time. This leaves a stale pointer in the linked
list which will result in a use-after-free in `FinalizeAll` during
environment teardown. The test below will fail if we make the above
change:

```
./node -e "require('./test/node-api/test_instance_data/build/Release/test_ref_then_set.node');"
```

Thus, we unlink a reference precisely when we destroy it – in its
destructor.

Refs: #34731
Refs: #34839
Refs: #35620
Refs: #35777
Fixes: #35778
Signed-off-by: Gabriel Schulhof <[email protected]>
PR-URL: #35933
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Zeyu Yang <[email protected]>
BethGriggs pushed a commit that referenced this pull request Dec 15, 2020
Currently, a reference is being unlinked from the list of references
tracked by the environment when `v8impl::Reference::Delete` is called.
This causes a leak when deletion must be deferred because the finalizer
hasn't yet run, but the finalizer does not run because environment
teardown is in progress, and so no more gc runs will happen, and the
`FinalizeAll` run that happens during environment teardown does not
catch the reference because it's no longer in the list. The test below
will fail when running with ASAN:

```
./node ./test/node-api/test_worker_terminate_finalization/test.js
```

OTOH if, to address the above leak, we make a special case to not
unlink a reference during environment teardown, we run into a
situation where the reference gets deleted by
`v8impl::Reference::Delete` but does not get unlinked because it's
environment teardown time. This leaves a stale pointer in the linked
list which will result in a use-after-free in `FinalizeAll` during
environment teardown. The test below will fail if we make the above
change:

```
./node -e "require('./test/node-api/test_instance_data/build/Release/test_ref_then_set.node');"
```

Thus, we unlink a reference precisely when we destroy it – in its
destructor.

Refs: #34731
Refs: #34839
Refs: #35620
Refs: #35777
Fixes: #35778
Signed-off-by: Gabriel Schulhof <[email protected]>
PR-URL: #35933
Reviewed-By: Rich Trott <[email protected]>
Reviewed-By: Michael Dawson <[email protected]>
Reviewed-By: Zeyu Yang <[email protected]>
@gabrielschulhof gabrielschulhof deleted the n-api-fix-delete-after-weaken branch January 28, 2021 00:14
@gabrielschulhof gabrielschulhof restored the n-api-fix-delete-after-weaken branch January 28, 2021 05:40
@gabrielschulhof gabrielschulhof deleted the n-api-fix-delete-after-weaken branch February 3, 2021 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. node-api Issues and PRs related to the Node-API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

N-API reference management leaks memory
7 participants